<?php
/**
 * Parse class annotations
 *
 * @author André Silva
 */
class Annotation {
	const PREFIX = '_cerberus_annotation_';
	public static $annotations = array();

	/**
	 * Get all annotations from a documentation
	 *
	 * @param string $documentation
	 * @return array
	 */
	public static function getAnnotation($documentation) {
		$annotation = array();
		if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]*(?P<value>.*?))?[ \t]*$/m', $documentation, $matches)) {
			$total = count($matches[0]);
			for ($i = 0; $i < $total; ++$i) {
				$name = trim($matches['name'][$i]);
				$value = trim($matches['value'][$i]);
				if (!empty ($value)) {
					if ($value[0] == '(') {
						$value = substr($value, 1, strlen($value)-2);
					}
					if (strpos($value, ',') !== false || strpos($value, '=') !== false) {
						$pieces = explode(',', $value);
						$value = array();
						foreach ($pieces as $piece) {
							$arr = explode('=', $piece);
							$value[trim($arr[0])] = trim($arr[1]);
						}
					}
				}
				$annotation[$name] = $value;
			}
		}
		return $annotation;
	}

	/**
	 * Get all annotations from a class and his properties
	 *
	 * @param string $className
	 * @return array
	 */
	public static function &forClass($className) {
		if (!isset (self::$annotations[$className])) {
			$annotations = Cache::get(self::PREFIX.$className);
			if ($annotations === null) {
				$class = new ReflectionClass($className);
				$annotations = array('annotation'=>self::getAnnotation($class->getDocComment()));
				$annotations['properties'] = array();
				$annotations['columns'] = array();
				$annotations['joinColumns'] = array();
				$annotations['relationships'] = array();

				$properties = $class->getProperties();
				foreach ($properties as $property) {
					if (!$property->isPrivate()) {
						continue;
					}
					$annotation = self::getAnnotation($property->getDocComment());
					if (array_key_exists('Column', $annotation)) {
						if (empty ($annotation['Column'])) {
							$annotations['columns'][$property->getName()] = $property->getName();
						} else {
							$annotations['columns'][$annotation['Column']] = $property->getName();
						}
					}
					foreach ($annotation as $type => $notation) {
						if ($type=='OneToOne'
								||$type=='OneToMany'
								||$type=='ManyToOne'
								||$type=='ManyToMany') {
							$annotations['relationships'][$property->getName()] = $type;
							if (array_key_exists('joinColumn', $annotation[$type]) && !array_key_exists($annotation[$type]['joinColumn'], $annotations['columns'])) {
								$annotations['joinColumns'][$annotation[$type]['joinColumn']][] = $property->getName();
							}
							if ($type=='ManyToOne') {
								$annotations['columns'][$annotation[$type]['joinColumn']] = null;
							}
						}
					}
					$annotations['properties'][$property->getName()] = $annotation;
				}
				$annotations['columns'] = array_unique($annotations['columns']);
				Cache::set(self::PREFIX.$className, $annotations);
			}
			self::$annotations[$className] = $annotations;
		}
		return self::$annotations[$className];
	}
}
?>
